#pragma once

#include "BlockQueue.hpp"
#include "../Locker/Locker.h"

#include <cstdio>
#include <string>

class TimeStamp;

enum LogLevel {
    DEBUG,
    INFO,
    ERROR,
    FATAL,
};

// output four MACROs
#define LOG_DEBUG(logmsgFormat, ...) \
    do { \
        if(0 == Log::getInstance()->closeLog()) { \
            Log::getInstance()->writeLog(DEBUG, logmsgFormat, ##__VA_ARGS__); \
        } \
    }while(0)

#define LOG_INFO(logmsgFormat, ...) \
    do { \
        if(0 == Log::getInstance()->closeLog()) { \
            Log::getInstance()->writeLog(INFO, logmsgFormat, ##__VA_ARGS__); \
        } \
    }while(0)

#define LOG_ERROR(logmsgFormat, ...) \
    do { \
        if(0 == Log::getInstance()->closeLog()) { \
            Log::getInstance()->writeLog(ERROR, logmsgFormat, ##__VA_ARGS__); \
        } \
    }while(0)

#define LOG_FATAL(logmsgFormat, ...) \
    do { \
        if(0 == Log::getInstance()->closeLog()) { \
            Log::getInstance()->writeLog(FATAL, logmsgFormat, ##__VA_ARGS__); \
            exit(-1);   \
        } \
    }while(0)

class Log
{
public:
    /**
     * < Hungry Singleton Design Pattern >
     * After C++11, static member variables are already thread-safe, 
     * so we dont't need to add locks
     * Hidden trouble: if the Log object isn't initialized, call Log::getInstance() function will 
     * result to the undefined behavior
    */
    static Log* getInstance();

    static void *flushLogThread(void *args);

    bool init(const char* fileName, int closeLog, int logBufSize = 8192, int splitLines = 500000, int maxQueueSize = 0);
    
    void writeLog(LogLevel level, const char* format, ...);

    void flush(void);

    const int closeLog() const { return closeLog_; }
private:
    Log();
    ~Log();

    void* asyncWriteLog();

    // 文件路径名
    char dirName_[128];
    // 文件名
    char logName_[128];
    // 日志的最大行数, 超过之后就创建新的文件
    int splitLines_;
    // 日志缓存区大小
    int logBufSize_;
    // 日志总行数记录
    long long count_;
    // 因为是按天进行分类，所以记录今天是哪一天
    TimeStamp* today_;
    // 打开log的文件指针
    FILE *fp_;
    // @TODO: Encapsulate as a class
    char* buf_; 
    // 阻塞队列
    BlockQueue<std::string> *logQueue_;
    // 同步位
    bool isAsync_;
    // lock
    Locker mutex_;
    // 关闭日志
    int closeLog_; // 0-open 1-close
};